package com.hmdp.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.dto.LoginFormDTO;
import com.hmdp.dto.Result;
import com.hmdp.dto.UserDTO;
import com.hmdp.entity.User;
import com.hmdp.mapper.UserMapper;
import com.hmdp.service.IUserService;
import com.hmdp.utils.RegexUtils;
import com.hmdp.utils.UserHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpSession;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.hmdp.utils.RedisConstants.*;
import static com.hmdp.utils.SystemConstants.USER_NICK_NAME_PREFIX;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author lwk
 * @since 2025-5-30
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public Result sendCode(String phone, HttpSession session) {
        //1.校验手机号
        if(RegexUtils.isPhoneInvalid(phone)){
            //2.如果不符合，返回错误信息
            return Result.fail("手机号格式错误！");
        }


        //3.符合，生成验证码
        String code = RandomUtil.randomNumbers(6);

        //4.保存验证码到redis
        stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY+phone, code,LOGIN_CODE_TTL, TimeUnit.MINUTES);

        //5.发送验证码
        log.debug("发送验证码成功！验证码为：{}",code);
        //6.返回ok
        return Result.ok();
    }

    @Override
    public Result login(LoginFormDTO loginForm, HttpSession session) {
        //1.校验手机号
        if(RegexUtils.isPhoneInvalid(loginForm.getPhone())){
            //如果不符合，返回错误信息
            return Result.fail("手机号格式错误！");
        }
        //2.校验验证码
        //从redis获取验证码
        String cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY+loginForm.getPhone());
        if (cacheCode == null || !cacheCode.equals(loginForm.getCode())){
            //3.不一致报错
            return Result.fail("验证码错误！");
        }
        //4.一致，根据手机号查询用户
        //构造查询条件
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getPhone,loginForm.getPhone());
        User user = this.getOne(queryWrapper);
        //5.判断用户是否存在
        if (user == null){
            //6.不存在，创建新用户并保存
            user = new User();
            user.setPhone(loginForm.getPhone());
            user.setNickName(USER_NICK_NAME_PREFIX + RandomUtil.randomString(10));
            save(user);

        }


        //7.保存用户到redis并返回ok
        //生成token，作为登录令牌
        String token = UUID.randomUUID(true).toString();
        //将User对象转为hash存储
        UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
        Map<String, Object> userMap = BeanUtil.beanToMap(userDTO, new HashMap<>(),
                CopyOptions.create()
                        .setIgnoreNullValue(true)
                        .setFieldValueEditor((fieldName,fieldValue) -> fieldValue.toString()));
        stringRedisTemplate.opsForHash().putAll(LOGIN_USER_KEY+token,userMap);
        //设置有效期
        stringRedisTemplate.expire(LOGIN_USER_KEY+token,LOGIN_USER_TTL,TimeUnit.MINUTES);

        return Result.ok(token);
    }

    @Override
    public User queryUserById(Long userId) {
        User user = getById(userId);
        return user;
    }

    //用户签到
    @Override
    public Result sign() {
        //1.获取用户信息
        Long userId = UserHolder.getUser().getId();
        //2.获取当前年月
        String now = LocalDateTime.now().format(DateTimeFormatter.ofPattern(":yyyyMM"));
        //3.拼接key
        String key = USER_SIGN_KEY+userId+now;

        //3.1获取当前是这个月的第几天
        int dayOfMonth = LocalDateTime.now().getDayOfMonth();
        //4.写入redis
        stringRedisTemplate.opsForValue().setBit(key,dayOfMonth-1,true);
        return Result.ok();
    }

    //统计用户签到
    @Override
    public Result signCount() {
        //1.获取用户信息
        Long userId = UserHolder.getUser().getId();
        //2.获取当前年月
        String now = LocalDateTime.now().format(DateTimeFormatter.ofPattern(":yyyyMM"));
        //3.拼接key
        String key = USER_SIGN_KEY+userId+now;

        //3.1获取当前是这个月的第几天
        int dayOfMonth = LocalDateTime.now().getDayOfMonth();
        //4.获取这个月到现在截至的所有签到记录，返回的是一个十进制数字
        List<Long> list = stringRedisTemplate.opsForValue().bitField(key, BitFieldSubCommands.create()
                .get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0));
        if (list == null || list.size() == 0){
            //没有签到结果
            return Result.ok(0);
        }
        Long num = list.get(0);
        if (num == 0){
          return Result.ok(0);
        }
        //5.循环遍历
        int count = 0;
        while (true){
            //5.1让这个数字值与1做与运算，得到数字的最后一个bit位
            //5.2判断是否为0，未签到继续循环
            if ((num & 1) == 0){
                break;
            }else {
                //5.3判断是否为1，已签到，计数器加1
                count++;

            }
            //5.4把数字右移以为，移除这个bit位
            num = num >> 1;
        }


        //6.返回结果
        return Result.ok(count);
    }


}
